package com.stock_t.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;

/**
 * Redis分布式锁工具类
 */
@Slf4j
@Component
public class RedisLockUtil {

    @Autowired
    private StringRedisTemplate redisTemplate;

    // 默认锁过期时间（秒）
    private static final long DEFAULT_LOCK_TIMEOUT = 2700;
    
    @PostConstruct
    public void init() {
        log.info("Redis分布式锁工具类初始化完成");
    }

    /**
     * 尝试获取锁
     *
     * @param lockKey 锁的键
     * @param requestId 请求标识（用于识别锁的持有者）
     * @param expireTime 锁的过期时间（秒）
     * @return 是否成功获取锁
     */
    public boolean tryLock(String lockKey, String requestId, long expireTime) {
        try {
            log.debug("尝试获取锁: key={}, requestId={}, expireTime={}s", lockKey, requestId, expireTime);
            Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireTime, TimeUnit.SECONDS);
            boolean acquired = result != null && result;
            log.debug("锁获取结果: {}", acquired ? "成功" : "失败");
            return acquired;
        } catch (Exception e) {
            log.error("获取Redis锁异常: key={}, error={}", lockKey, e.getMessage(), e);
            return false;
        }
    }

    /**
     * 尝试获取锁（使用默认过期时间）
     *
     * @param lockKey 锁的键
     * @param requestId 请求标识
     * @return 是否成功获取锁
     */
    public boolean tryLock(String lockKey, String requestId) {
        return tryLock(lockKey, requestId, DEFAULT_LOCK_TIMEOUT);
    }

    /**
     * 释放锁
     *
     * @param lockKey 锁的键
     * @param requestId 请求标识
     * @return 是否成功释放锁
     */
    public boolean releaseLock(String lockKey, String requestId) {
        try {
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
            // 使用更兼容的方式执行Lua脚本
            Object result = redisTemplate.execute(
                (connection) -> connection.scriptingCommands().eval(
                    script.getBytes(),
                    org.springframework.data.redis.connection.ReturnType.INTEGER,
                    1,
                    lockKey.getBytes(),
                    requestId.getBytes()
                ), 
                true
            );
            
            return result != null && (Long)result > 0;
        } catch (Exception e) {
            log.error("释放Redis锁异常", e);
            return false;
        }
    }

    /**
     * 强制释放锁（不检查持有者）
     *
     * @param lockKey 锁的键
     * @return 是否成功释放锁
     */
    public boolean forceReleaseLock(String lockKey) {
        try {
            Boolean result = redisTemplate.delete(lockKey);
            return result != null && result;
        } catch (Exception e) {
            log.error("强制释放Redis锁异常", e);
            return false;
        }
    }

    /**
     * 检查锁是否存在
     *
     * @param lockKey 锁的键
     * @return 锁是否存在
     */
    public boolean isLocked(String lockKey) {
        try {
            return redisTemplate.hasKey(lockKey);
        } catch (Exception e) {
            log.error("检查Redis锁状态异常", e);
            return false;
        }
    }

    /**
     * 获取锁的持有者
     *
     * @param lockKey 锁的键
     * @return 锁的持有者标识
     */
    public String getLockOwner(String lockKey) {
        try {
            return redisTemplate.opsForValue().get(lockKey);
        } catch (Exception e) {
            log.error("获取Redis锁持有者异常", e);
            return null;
        }
    }

    /**
     * 更新锁的过期时间
     *
     * @param lockKey 锁的键
     * @param expireTime 新的过期时间（秒）
     * @return 是否成功更新
     */
    public boolean updateLockExpiration(String lockKey, long expireTime) {
        try {
            Boolean result = redisTemplate.expire(lockKey, expireTime, TimeUnit.SECONDS);
            return result != null && result;
        } catch (Exception e) {
            log.error("更新Redis锁过期时间异常", e);
            return false;
        }
    }

    /**
     * 释放锁（备选方法）
     * 当原始方法出现问题时使用此方法
     *
     * @param lockKey 锁的键
     * @param requestId 请求标识
     * @return 是否成功释放锁
     */
    public boolean releaseLockAlternative(String lockKey, String requestId) {
        try {
            // 先获取锁的当前值
            String currentValue = redisTemplate.opsForValue().get(lockKey);
            
            // 如果锁存在且持有者是当前请求，则删除锁
            if (requestId.equals(currentValue)) {
                return redisTemplate.delete(lockKey);
            }
            
            return false;
        } catch (Exception e) {
            log.error("备选方法释放Redis锁异常", e);
            return false;
        }
    }
    
    /**
     * 智能释放锁（尝试多种方法）
     *
     * @param lockKey 锁的键
     * @param requestId 请求标识
     * @return 是否成功释放锁
     */
    public boolean smartReleaseLock(String lockKey, String requestId) {
        try {
            // 先尝试使用Lua脚本方式
            boolean released = releaseLock(lockKey, requestId);
            
            // 如果失败，尝试备选方法
            if (!released) {
                released = releaseLockAlternative(lockKey, requestId);
            }
            
            return released;
        } catch (Exception e) {
            log.error("智能释放Redis锁异常", e);
            
            // 出现异常时尝试备选方法
            try {
                return releaseLockAlternative(lockKey, requestId);
            } catch (Exception ex) {
                log.error("备选方法也失败", ex);
                return false;
            }
        }
    }
} 